package raft

import "time"

// RequestVoteArgs
// example RequestVote RPC arguments structure.
// field names must start with capital letters!
// 发送给其他节点的投票请求参数
type RequestVoteArgs struct {
	Term         int // Candidate的 term
	CandidateId  int // Candidate的 ID
	LastLogIndex int // Candidate最后一个 LogEntry 的 Index
	LastLogTerm  int // Candidate最后一个 LogEntry 的 Term
}

// RequestVoteReply
// example RequestVote RPC reply structure.
// field names must start with capital letters!
// 其他节点返回的投票响应
type RequestVoteReply struct {
	Term        int  // 当前节点的 term
	VoteGranted bool // 是否投票给了候选人
}

func (rf *Raft) RequestVote(args *RequestVoteArgs, reply *RequestVoteReply) error {
	// Your code here (2A, 2B).
	rf.mu.Lock()
	defer rf.mu.Unlock()
	defer rf.persist()

	// 获取 candidateId 和 term
	candidateId := args.CandidateId
	candidateTerm := args.Term

	// DPrintf("RequestVote(): server[%d] receive the candidate[%d] request", rf.me, candidateId)

	// paper: Reply false if term < currentTerm (§5.1)
	// 如果 candidate 的 term 小于 当前节点的 term，过期的竞选请求，直接返回
	if candidateTerm < rf.currentTerm {
		reply.Term = rf.currentTerm
		return nil
	}

	// paper: If votedFor is null or candidateId,
	// and candidate’s log is at least as up-to-date as receiver’s log, grant vote (§5.2, §5.4)

	// 如果候选人的 term 大于当前节点的 term，则将当前节点转换为 follower
	// 可能出现的情况：当前节点也可能是一个 竞选失败的 candidate 或者是由于网络分区出现的过期 leader
	if candidateTerm > rf.currentTerm {
		rf.state = Follower
		rf.votedFor = -1
		rf.currentTerm = candidateTerm
	}

	reply.Term = rf.currentTerm

	// 获取 candidate 最后一个 LogEntry 的 index 和 term
	candidateLastLogIndex := args.LastLogIndex
	candidateLastLogTerm := args.LastLogTerm
	// 当前节点最后一个 LogEntry 的 index 和 term
	localLastLogIndex := rf.GetLogEntry(-1).Index
	localLastLogTerm := rf.GetLogEntry(-1).Term

	// candidate最后一个 LogEntry 的 Term 小于当前节点最后一个 LogEntry 的 Term，则直接返回
	if candidateLastLogTerm < localLastLogTerm {
		return nil
	}

	// candidate 最后一个 LogEntry 的 term 和 当前节点的 最后一个 logEntry 相等，
	// 但是 candidate 的 index 小于当前节点的 index
	if candidateLastLogTerm == localLastLogTerm && candidateLastLogIndex < localLastLogIndex {
		return nil
	}

	// 当前节点还没有投票给其他节点，或者已经投票给了当前请求的 candidate，给予选票
	if rf.votedFor == -1 || rf.votedFor == candidateId {
		// DPrintf("RequestVote(): server[%d] vote for candidate[%d]", rf.me, candidateId)
		rf.lastHeartbeat = time.Now()
		rf.votedFor = candidateId
		reply.VoteGranted = true
	}
	return nil
}

func (rf *Raft) candidateSendRequestVote(server int) {
	rf.mu.RLock()

	// 如果当前节点不是Candidate，则直接返回
	if rf.state != Candidate {
		rf.mu.RUnlock()
		return
	}

	// 构造投票请求参数
	requestVoteArgs := RequestVoteArgs{
		Term:         rf.currentTerm,
		CandidateId:  rf.me,
		LastLogIndex: rf.GetLogEntry(-1).Index,
		LastLogTerm:  rf.GetLogEntry(-1).Term,
	}
	rf.mu.RUnlock()
	requestVoteReply := RequestVoteReply{}

	// 发起投票
	rf.sendRequestVote(server, &requestVoteArgs, &requestVoteReply)

	rf.mu.Lock()

	term := requestVoteReply.Term
	voteGranted := requestVoteReply.VoteGranted

	// 处理投票响应
	if term > rf.currentTerm {
		// DPrintf("candidateSendRequestVote(): server[%d] reply term newer than candidate[%d], become follower", server, rf.me)
		// 如果收到的响应的 term 大于当前节点的 term，则将当前节点转换为 follower
		rf.state = Follower
		rf.votedFor = -1
		rf.currentTerm = term
		rf.persist()
	}

	// 如果当前节点不是候选人或者收到的响应的 term 不等于当前节点的 term，则直接返回
	if rf.state != Candidate || term != rf.currentTerm {
		rf.mu.Unlock()
		return
	}

	// 如果收到响应中包含投票，当前节点投票数+1
	if voteGranted {
		// DPrintf("candidateSendRequestVote(): server[%d] receive server[%d] vote", rf.me, server)
		rf.voteCount++
	}
	// 如果当前节点的投票数超过了半数，则将当前节点转换为 leader
	if rf.voteCount == len(rf.peers)/2+1 {
		// DPrintf("candidateSendRequestVote(): server[%d] receive majority votes, become new leader", rf.me)
		rf.state = Leader
		for i := 0; i < len(rf.peers); i++ {
			rf.nextIndex[i] = rf.GetLogEntry(-1).Index + 1
			rf.matchIndex[i] = 0
		}
		rf.mu.Unlock()

		// 向其他节点发送心跳，告知新的Leader已经诞生
		for server := range rf.peers {
			if server == rf.me {
				continue
			}
			go rf.leaderSendAppendEntries(server)
		}
	} else {
		rf.mu.Unlock()
	}

}

func (rf *Raft) sendRequestVote(server int, args *RequestVoteArgs, reply *RequestVoteReply) bool {
	ok := rf.peers[server].Call("Raft.RequestVote", args, reply)
	return ok
}
